﻿using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using SlamCms.Common;
using SlamCms.Data;
using SlamCms.Web;

namespace SlamCms.Data
{
	public static class TagTreeExtensions2
	{
		public static TagTree ForTagBrowser(this TagTree tagTree, Controller controller, TagBrowserParameters parameters)
		{
			tagTree.UpdateForTagBrowser(controller.Url, parameters);
			return tagTree;
		}

		public static TagTree ForTagBrowser(this TagTree tagTree, Controller controller, string tagBrowserId)
		{
			tagTree.UpdateForTagBrowser(controller.Url, tagBrowserId);
			return tagTree;
		}

		public static IEnumerable<TagTreeNode> ForFlyout(this TagTree tagTree, int columns, int margin = 0)
		{
			var itemsCount = tagTree.Root.Children.Count + tagTree.Root.Children.Sum(t => t.Children.Count);
			var itemsPerColumn = itemsCount / columns + margin;

			var list = new List<TagTreeNode>();
			var currentColumn = ForFlyoutAddColumn(list);

			foreach (var tagCategory in tagTree.Root.Children)
			{
				var currentItemsInColumn = currentColumn.Children.Count + currentColumn.Children.Sum(t => t.Children.Count);
				if (currentItemsInColumn + tagCategory.Children.Count + 1 > itemsPerColumn)
					currentColumn = ForFlyoutAddColumn(list);
				currentColumn.Children.Add(tagCategory);
			}

			return list;
		}

		private static TagTreeNode ForFlyoutAddColumn(List<TagTreeNode> list)
		{
			var columnNumber = list.Count + 1;
			var newColumn = new TagTreeNode()
			{
				Id = "column{0}".F(columnNumber),
				Text = "Column {0}".F(columnNumber),
				Type = TagTreeNodeType.Special
			};

			list.Add(newColumn);
			return list.Last();
		}

		private static void UpdateForTagBrowser(this TagTree tagTree, UrlHelper url, TagBrowserParameters parameters)
		{
			// first step to mark selected nodes
			SlamCms.Data.TagTreeExtensions.ApplyToTagTreeNode(tagTree.Root, n =>
			{
				n.IsSelected = n.Identifiers.Length > 0 && parameters.FilteredTags.Length > 0 && n.Identifiers.Length == parameters.FilteredTags.Count(t => t.In(n.Identifiers));
			});

			// second step to build the url for each node
			SlamCms.Data.TagTreeExtensions.ApplyToTagTreeNode(tagTree.Root, n =>
			{
				if (n.IsSelected || n == tagTree.Root)
				{
					var tagsToRemove = new List<string>();
					tagsToRemove.Add(n.Identifier);

					foreach (var childNode in n.GetAllChildren())
					{
						if (tagTree.FindNode(x => x.Identifier == childNode.Identifier && x.Id != childNode.Id && x.IsSelected) == null)
							tagsToRemove.Add(childNode.Identifier);
					}

					n.Url = url.TagBrowser(parameters).RemoveTags(tagsToRemove.ToArray()).Render(); //new String[] { n.Identifiers.Last() }
				}
				else
					n.Url = url.TagBrowser(parameters).AddTags(n.Identifiers).Render();
			});

			// add orphan selected tags
			AddOrphanSelectedTags(tagTree, url, parameters);
		}

		private static IEnumerable<TagTreeNode> GetAllChildren(this TagTreeNode node)
		{
			var list = new List<TagTreeNode>();

			foreach (var childNode in node.Children)
			{
				list.Add(childNode);
				list.AddRange(childNode.GetAllChildren());
			}

			return list;
		}

		private static void UpdateForTagBrowser(this TagTree tagTree, UrlHelper url, string tagBrowserId)
		{
			SlamCms.Data.TagTreeExtensions.ApplyToTagTreeNode(tagTree.Root, n =>
			{
				n.Url = url.TagBrowser(tagBrowserId).AddTags(n.Identifiers).Render();
			});
		}

		private static void AddOrphanSelectedTags(TagTree tagTree, UrlHelper url, TagBrowserParameters parameters)
		{
			var orphanSelectedTags = tagTree.FindNodes(x => !x.IsSelected
														 && x.Type == TagTreeNodeType.Tag
														 && x.Tag.Identifier.In(parameters.FilteredTags)
														 && x.Parent != null
														 && x.Parent.Type == TagTreeNodeType.Tag
														 && !x.Parent.IsSelected
														 && tagTree.FindNode(y => y.Identifier == x.Identifier && y.Id != x.Id && y.IsSelected) == null)
											.Select(x => x.Tag)
											.Distinct();

			if (!orphanSelectedTags.Any())
				return;

			var orphanTagCategory = new TagTreeNode()
			{
				Id = "orphans",
				Text = string.Empty,
				Parent = tagTree.Root,
				Type = TagTreeNodeType.TagCategory
			};

			foreach (var tag in orphanSelectedTags)
			{
				var orphanTagNode = new TagTreeNode()
				{
					IsSelected = true,
					Id = "orphan{0}".F(tag.Identifier),
					Text = tag.DisplayName ?? tag.Identifier,
					Parent = orphanTagCategory,
					Type = TagTreeNodeType.Tag,
					Url = url.TagBrowser(parameters).RemoveTags(tag.Identifier).Render(),
					ContentItemCount = 1 // Not to be removed
				};

				orphanTagCategory.Children.Add(orphanTagNode);
			}

			tagTree.Root.Children.Insert(0, orphanTagCategory);
		}
	}
}